package com.geodatastore.filesystem;

import com.alibaba.fastjson.JSONObject;
import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;

import javax.swing.*;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Csv2Shape {
    private File file;
    private SimpleFeatureType simpleFeatureType;
    private ShapefileDataStore shapefileDataStore;
    public  Csv2Shape() throws Exception {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        this.file = JFileDataStoreChooser.showOpenFile("csv", null);
        this.simpleFeatureType =
                DataUtilities.createType(
                        "Location",
                        "the_geom:Point:srid=4326,"
                                + // <- the geometry attribute: Point type
                                "name:String,"
                                + // <- a String attribute
                                "number:Integer" // a number attribute
                );
    }

    /*public SimpleFeatureType createType() throws Exception {
        SimpleFeatureType simpleFeatureType = null;
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        file = JFileDataStoreChooser.showOpenFile("csv", null);
        if (file == null) {
            return simpleFeatureType;
        }
        *//*
         * We use the DataUtilities class to create a FeatureType that will describe the data in our
         * shapefile.
         *
         * See also the createFeatureType method below for another, more flexible approach.
         *//*
        simpleFeatureType =
                DataUtilities.createType(
                        "Location",
                        "the_geom:Point:srid=4326,"
                                + // <- the geometry attribute: Point type
                                "name:String,"
                                + // <- a String attribute
                                "number:Integer" // a number attribute
                );
        return simpleFeatureType;
    }*/

    public List<SimpleFeature> getSimpleFeatures() throws Exception{
        /*
         * A list to collect features as we create them.
         */
        List<SimpleFeature> features = new ArrayList<>();
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(this.simpleFeatureType);

        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))){
            /* First line of the data file is the header */
            String line = bufferedReader.readLine();
            System.out.println("Header: " + line);
            for (line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
                if (line.trim().length() > 0) { // skip blank lines
                    String[] tokens = line.split("\\,");

                    double latitude = Double.parseDouble(tokens[0]);
                    double longitude = Double.parseDouble(tokens[1]);
                    String name = tokens[2].trim();
                    int number = Integer.parseInt(tokens[3].trim());

                    /* Longitude (= x coord) first ! */
                    Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));

                    featureBuilder.add(point);
                    featureBuilder.add(name);
                    featureBuilder.add(number);
                    SimpleFeature feature = featureBuilder.buildFeature(null);
                    features.add(feature);
                }
            }
        }
        return features;
    }

    public ShapefileDataStore createShpDataStore() throws Exception {
        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
        File shpFile = getNewShapeFile(file);

        Map<String, Serializable> params = new HashMap<>();
        params.put("url", shpFile.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);

        ShapefileDataStore shapefileDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);

        shapefileDataStore.createSchema(this.simpleFeatureType);
        this.shapefileDataStore = shapefileDataStore;
        return shapefileDataStore;
    }

    public void writeFeaturesToShp() throws Exception {
        Transaction transaction = new DefaultTransaction("create");
        ShapefileDataStore shapefileDataStore = createShpDataStore();
        String typeName = shapefileDataStore.getTypeNames()[0];
        SimpleFeatureSource simpleFeatureSource = shapefileDataStore.getFeatureSource();
        SimpleFeatureType SHAPE_TYPE = simpleFeatureSource.getSchema();
        /*
         * The Shapefile format has a couple limitations:
         * - "the_geom" is always first, and used for the geometry attribute name
         * - "the_geom" must be of type Point, MultiPoint, MuiltiLineString, MultiPolygon
         * - Attribute names are limited in length
         * - Not all data types are supported (example Timestamp represented as Date)
         *
         * Each data store has different limitations so check the resulting SimpleFeatureType.
         */
        System.out.println("SHAPE:" + SHAPE_TYPE);

        if (simpleFeatureSource instanceof SimpleFeatureStore) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) simpleFeatureSource;
            SimpleFeatureCollection collection = new ListFeatureCollection(this.simpleFeatureType, getSimpleFeatures());
            featureStore.setTransaction(transaction);
            try {
                featureStore.addFeatures(collection);
                transaction.commit();
            } catch (IOException e) {
                e.printStackTrace();
                transaction.rollback();
            } finally {
                transaction.close();
            }

        } else {
            System.out.println(typeName + " does not support read/write access");
            System.exit(1);
        }
    }

    /**
     * 查询shp数据根据名称
     * @param locationName
     * @return
     * @throws IOException
     * @throws CQLException
     */
    public SimpleFeatureCollection queryShpFeaturesByName(String locationName) throws IOException, CQLException {
        List attrs = new ArrayList();
        SimpleFeatureCollection simpleFeatureCollection = null;
        SimpleFeatureStore featureStore = (SimpleFeatureStore) this.shapefileDataStore.getFeatureSource();
        Filter filter = CQL.toFilter( "name='" + locationName + "'");
        simpleFeatureCollection = featureStore.getFeatures(filter);
        SimpleFeatureIterator simpleFeatureIterator = simpleFeatureCollection.features();
        FeatureJSON fjson = new FeatureJSON();
        while (simpleFeatureIterator.hasNext()) {
            SimpleFeature feature = simpleFeatureIterator.next();
            StringWriter writer = new StringWriter();
            fjson.writeFeature(feature, writer);
            String json = writer.toString();
            System.out.println(json);
            attrs.add(JSONObject.parseObject(json));
        }
        return  simpleFeatureCollection;
    }


    /**
     * Prompt the user for the name and path to use for the output shapefile
     *
     * @param csvFile the input csv file used to create a default shapefile name
     * @return name and path for the shapefile as a new File object
     */
    private static File getNewShapeFile(File csvFile) {
        String path = csvFile.getAbsolutePath();
        String newPath = path.substring(0, path.length() - 4) + ".shp";

        JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
        chooser.setDialogTitle("Save shapefile");
        chooser.setSelectedFile(new File(newPath));

        int returnVal = chooser.showSaveDialog(null);

        if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
            // the user cancelled the dialog
            System.exit(0);
        }

        File newFile = chooser.getSelectedFile();
        if (newFile.equals(csvFile)) {
            System.out.println("Error: cannot replace " + csvFile);
            System.exit(0);
        }

        return newFile;
    }

    public static void main(String[] args) throws Exception {
        Csv2Shape csv2Shape = new Csv2Shape();
//        List<SimpleFeature> features = csv2Shape.getSimpleFeatures();
//        ShapefileDataStore shapefileDataStore = csv2Shape.createShpDataStore();
        csv2Shape.writeFeaturesToShp();
        SimpleFeatureCollection simpleFeatureCollection = csv2Shape.queryShpFeaturesByName("Denver");
    }
    
    
}
